home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume22 / nn6.4 / part13 < prev    next >
Encoding:
Internet Message Format  |  1990-06-07  |  54.1 KB

  1. Subject:  v22i048:  NN Newsreader, release 6.4, Part13/21
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: 748490dd 2edefe04 18c447de 314e980c
  5.  
  6. Submitted-by: "Kim F. Storm" <storm@texas.dk>
  7. Posting-number: Volume 22, Issue 48
  8. Archive-name: nn6.4/part13
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then feed it
  12. # into a shell via "sh file" or similar.  To overwrite existing files,
  13. # type "sh file -c".
  14. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  15. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  16. # Contents:  db.c folder.c man/nnmaster.8
  17. # Wrapped by storm@texas.dk on Sun May  6 18:19:53 1990
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. echo If this archive is complete, you will see the following message:
  20. echo '          "shar: End of archive 13 (of 22)."'
  21. if test -f 'db.c' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'db.c'\"
  23. else
  24.   echo shar: Extracting \"'db.c'\" \(20768 characters\)
  25.   sed "s/^X//" >'db.c' <<'END_OF_FILE'
  26. X/*
  27. X *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  28. X *
  29. X *    Database access and update
  30. X */
  31. X
  32. X#include "config.h"
  33. X#include "db.h"
  34. X
  35. Ximport char
  36. X    *master_directory, *db_directory, *db_data_directory, *news_directory;
  37. X
  38. Xexport master_header master;
  39. Xexport group_header *active_groups = NULL;
  40. Xexport group_header **sorted_groups = NULL;
  41. X
  42. Xexport int  reread_groups_file = 0;  /* nnmaster -G */
  43. X
  44. Xexport data_header db_hdr;
  45. Xexport data_dynamic_data db_data;
  46. X
  47. Xexport int32 db_read_counter = 0;     /* articles read by db_read_art */
  48. Xexport int32 db_write_counter = 0;    /* articles written by db_write_art */
  49. X
  50. X/*
  51. X * Init access to a group
  52. X */
  53. X
  54. Xexport group_header *current_group = NULL;
  55. X
  56. Xexport char group_path_name[FILENAME];
  57. Xexport char *group_file_name = NULL;
  58. X
  59. Xstatic char *group_position = NULL;
  60. Xstatic article_number current_digest_article = 0;
  61. X
  62. Xinit_group(gh)
  63. Xregister group_header *gh;
  64. X{
  65. X    register char *p, *q;
  66. X
  67. X    current_digest_article = 0;
  68. X
  69. X    if (gh == NULL) return 0;
  70. X    if (gh->master_flag & M_IGNORE_GROUP) return 0;
  71. X    if (gh == current_group) return 1;
  72. X
  73. X    current_group = gh;
  74. X
  75. X    if (gh->group_flag & G_FOLDER) {
  76. X    group_position = NULL;
  77. X    group_file_name = NULL;
  78. X    strcpy(group_path_name, gh->group_name);
  79. X    return 1;
  80. X    }
  81. X
  82. X#ifdef NNTP
  83. X    if (use_nntp && nntp_set_group(gh) < 0)
  84. X    return 0;
  85. X#endif /* NNTP */
  86. X
  87. X    if (group_position == NULL)
  88. X    if (who_am_i == I_AM_MASTER || who_am_i == I_AM_EXPIRE)
  89. X        group_position = group_path_name;
  90. X    else {
  91. X        strcpy(group_path_name, news_directory);
  92. X        group_position = group_path_name + strlen(group_path_name);
  93. X        *group_position++ = '/';
  94. X    }
  95. X
  96. X    for (p = group_position, q = gh->group_name; *q; q++)
  97. X    *p++ = (*q == '.') ? '/' : *q;
  98. X
  99. X    if (who_am_i == I_AM_MASTER) {
  100. X
  101. X    /* The master will chdir to the group's directory to get */
  102. X    /* better performance (can use relative path names). */
  103. X
  104. X    *p++ = NUL;
  105. X
  106. X    if (!use_nntp) {
  107. X        if (chdir(news_directory) < 0)
  108. X        sys_error(news_directory);
  109. X
  110. X        if (chdir(group_path_name) < 0)
  111. X        return 0;
  112. X    }
  113. X    group_file_name = group_path_name;
  114. X    return 1;
  115. X    }
  116. X
  117. X    /* client */
  118. X    if (gh->master_flag & M_NO_DIRECTORY) return 0;
  119. X
  120. X    *p++ = '/';
  121. X    *p = NUL;
  122. X    group_file_name = p;
  123. X    return 1;
  124. X}
  125. X
  126. X/*
  127. X *    Open master & group file; read it in if first open.
  128. X *
  129. X *    GROUPS file format is:
  130. X *
  131. X *    One line per group:
  132. X *    <group name> [<space><timestamp>] [<space><options>] <NL>
  133. X *    If <timestamp> is omitted, a timestamp of 0 is used (very old group).
  134. X *    If <group name> is "@", the master entry is ignored.
  135. X *
  136. X *    <options>:
  137. X *    D    Digest all articles in the group
  138. X *    N    Never digest articles
  139. X *    @    Bogus group, ignore completely
  140. X *    !    Don't collect this group
  141. X *
  142. X *    Do not edit the GROUPS file while nnmaster is running.
  143. X *    After editing the groups file (options), run nnmaster -G.
  144. X */
  145. X
  146. Xstatic FILE *group_file = NULL;
  147. X
  148. Xopen_groups(mode)
  149. Xint mode;
  150. X{
  151. X    group_file = open_file(relative(db_directory, "GROUPS"), mode);
  152. X
  153. X    if (group_file != NULL && (mode & OPEN_CREATE)) {
  154. X    fprintf(group_file, 
  155. X"#\n#\tRUN 'nnmaster -G' AFTER MODIFYING THIS FILE\n");
  156. X    fprintf(group_file, 
  157. X"#\n#\tDO NOT REMOVE OR REORDER ANY LINES IN THIS FILE\n#\n");
  158. X    }
  159. X    
  160. X    return group_file != NULL;
  161. X}
  162. X
  163. Xclose_groups()
  164. X{
  165. X    if (group_file != NULL) {
  166. X    fclose(group_file);
  167. X    group_file = NULL;
  168. X    }
  169. X}
  170. X
  171. Xdb_append_group(gh)
  172. Xregister group_header *gh;
  173. X{
  174. X    char flags[16], *fp;
  175. X    int was_open = (group_file != NULL);
  176. X
  177. X    if (!was_open) open_groups(OPEN_UPDATE|MUST_EXIST);
  178. X
  179. X    fseek(group_file, (off_t)0, 2);
  180. X    if (gh->group_name[0] == NUL) {
  181. X    fputc('@', group_file);
  182. X    goto out;
  183. X    }
  184. X
  185. X    fprintf(group_file, "%s", gh->group_name);
  186. X    if (gh->creation_time > 0)
  187. X    fprintf(group_file, " %ld", (long)(gh->creation_time));
  188. X
  189. X    fp = flags;
  190. X
  191. X    if (gh->master_flag & M_IGNORE_G)
  192. X    *fp++ = '!';
  193. X    if (gh->master_flag & M_ALWAYS_DIGEST)
  194. X    *fp++ = 'D';
  195. X    if (gh->master_flag & M_NEVER_DIGEST)
  196. X    *fp++ = 'N';
  197. X    if (gh->master_flag & M_INCLUDE_OLD)
  198. X    *fp++ = 'O';
  199. X    if (gh->master_flag & M_AUTO_RECOLLECT)
  200. X    *fp++ = 'R';
  201. X    if (gh->archive_file != NULL)
  202. X    *fp++ = '>';
  203. X
  204. X    if (fp != flags) {
  205. X    *fp++ = NUL;
  206. X    fprintf(group_file, " %s%s", flags,
  207. X        gh->archive_file != NULL ? gh->archive_file : "");
  208. X    }
  209. X
  210. X out:
  211. X    fputc(NL, group_file);
  212. X    fflush(group_file);
  213. X
  214. X    if (!was_open) close_groups();
  215. X}
  216. X
  217. Xstatic char *mk_archive_file(gh, cp, logerr)
  218. Xregister group_header *gh;
  219. Xregister char *cp;
  220. Xint logerr;
  221. X{
  222. X    char *name;
  223. X
  224. X    while (*cp && (*cp == '>' || isspace(*cp))) cp++;
  225. X
  226. X    name = cp;
  227. X    while (*cp && !isspace(*cp)) cp++;
  228. X    if (*cp) *cp++ = NUL;
  229. X    if (*name) {
  230. X    gh->archive_file = copy_str(name);
  231. X
  232. X    if (*name == '/')
  233. X        gh->master_flag |= M_AUTO_ARCHIVE;
  234. X    else {
  235. X        gh->master_flag &= ~M_AUTO_ARCHIVE;
  236. X        if (who_am_i == I_AM_MASTER)
  237. X        if (logerr)
  238. X            log_entry('E', "GROUPS %s >%s: Full path required",
  239. X                  gh->group_name, name);
  240. X        else
  241. X            printf("Error in GROUPS: %s >%s: Full path required\n",
  242. X                  gh->group_name, name);
  243. X    }
  244. X    }
  245. X
  246. X    return cp;
  247. X}
  248. X
  249. Xdb_parse_group(gh, trust_master)
  250. Xregister group_header *gh;
  251. Xint trust_master;        /* trust what is in the master file */
  252. X{
  253. X    char line[256];
  254. X    register char *cp, *name;
  255. X    int ignore;
  256. X
  257. X    do {
  258. X    if (fgets(line, 256, group_file) == NULL) return 0;
  259. X    for (cp = line; *cp && isspace(*cp); cp++);
  260. X    } while (*cp == NUL || *cp == '#');
  261. X
  262. X    gh->archive_file = NULL;
  263. X
  264. X    name = cp;
  265. X
  266. X    if (trust_master) {
  267. X    cp = name + gh->group_name_length;
  268. X    } else {
  269. X    /* parse GROUPS line */
  270. X
  271. X    if (*cp == '@') {
  272. X        ignore = 1;
  273. X        gh->group_name_length = 0;
  274. X        gh->group_name = "";
  275. X        goto ignore_group;
  276. X    }
  277. X
  278. X    if (gh->group_name_length == 0) {
  279. X        while (*cp && !isspace(*cp)) cp++;
  280. X        gh->group_name_length = cp - name;
  281. X    } else {
  282. X        cp = name + gh->group_name_length;
  283. X        if (*cp == NUL || !isspace(*cp)) {
  284. X        sys_error("MASTER/GROUPS conflict: %d/%s", gh->group_num, line);
  285. X        }
  286. X    }
  287. X    }
  288. X
  289. X    if (*cp) *cp++ = NUL;
  290. X    if (gh->group_name_length > 0)
  291. X    gh->group_name = copy_str(name);
  292. X    else
  293. X    gh->group_name = "";
  294. X
  295. X    if (trust_master) {
  296. X    if (gh->master_flag & M_AUTO_ARCHIVE) {
  297. X        while (*cp && *cp != '>') cp++;
  298. X        if (*cp == '>') cp = mk_archive_file(gh, cp, 1);
  299. X    }
  300. X    return 1;
  301. X    }
  302. X
  303. X    while (*cp && isspace(*cp)) cp++;
  304. X
  305. X    if (*cp && isdigit(*cp)) {
  306. X    gh->creation_time = atol(cp);
  307. X    while (*cp && isdigit(*cp)) cp++;
  308. X    } else
  309. X    gh->creation_time = 0;
  310. X
  311. X    while (*cp && isspace(*cp)) cp++;
  312. X
  313. X    ignore = 0;
  314. X    gh->master_flag &= ~(M_ALWAYS_DIGEST | M_NEVER_DIGEST |
  315. X            M_AUTO_RECOLLECT | M_AUTO_ARCHIVE);
  316. X
  317. X    while (*cp) {
  318. X    switch (*cp++) {
  319. X     case ' ':
  320. X     case '\t':
  321. X     case NL:
  322. X     case CR:
  323. X        continue;
  324. X
  325. X     case 'D':    /* Collect this group, digest all articles */
  326. X        gh->master_flag |= M_ALWAYS_DIGEST;
  327. X        continue;
  328. X
  329. X     case 'N':    /* Collect this group, never digest articles */
  330. X        gh->master_flag |= M_NEVER_DIGEST;
  331. X        continue;
  332. X
  333. X     case 'O':    /* Ignore -O option for this group */
  334. X        gh->master_flag |= M_INCLUDE_OLD;
  335. X        continue;
  336. X
  337. X     case 'R':    /* Recollect this group when new articles arrive */
  338. X        gh->master_flag |= M_AUTO_RECOLLECT;
  339. X        continue;
  340. X
  341. X     case '>':    /* Archive all new articles in gh->archive_file */
  342. X        cp = mk_archive_file(gh, cp, 0);
  343. X        continue;
  344. X
  345. X     case '@':    /* Bogus GROUP -- ignore completely */
  346. X        ignore = 1;
  347. X        gh->group_name_length = 0;
  348. X        break;
  349. X
  350. X     case '!':    /* Do not collect this group */
  351. X     case 'X':
  352. X        ignore = 1;
  353. X        break;
  354. X
  355. X     case '#':    /* comment */
  356. X        *cp = NUL;
  357. X        break;
  358. X
  359. X     default:
  360. X        printf("Bad GROUPS flag for %s: `%c'\n", gh->group_name, *--cp);
  361. X        break;
  362. X    }
  363. X    break;
  364. X    }
  365. X
  366. X ignore_group:
  367. X
  368. X    /* G_DONE indicates to master that the group must be cleaned */
  369. X
  370. X    if (ignore) {
  371. X    if ((gh->master_flag & M_IGNORE_GROUP) == 0) {
  372. X        gh->master_flag |= M_MUST_CLEAN;
  373. X        log_entry('X', "Group %s ignored", gh->group_name);
  374. X    }
  375. X    gh->master_flag |= M_IGNORE_G;
  376. X    } else {    /* was group ignored in GROUPS, but not active before? */
  377. X    if ((gh->master_flag & M_IGNORE_GROUP) == M_IGNORE_G) {
  378. X        gh->master_flag &= ~M_NO_DIRECTORY;
  379. X        log_entry('X', "Group %s activated", gh->group_name);
  380. X    }
  381. X    gh->master_flag &= ~M_IGNORE_G;
  382. X    }
  383. X    
  384. X    return 1;
  385. X}
  386. X
  387. X/*
  388. X *    Open master & group files; read then in if first open.
  389. X */
  390. X
  391. Xstatic FILE *master_file = NULL;
  392. Xstatic int db_sequential = 0;
  393. X
  394. Xopen_master(mode)
  395. Xint mode;
  396. X{
  397. X    register group_header *gh;
  398. X    int trust_master;
  399. X
  400. X    close_master();
  401. X
  402. X    master_file = open_file(relative(db_directory, "MASTER"), mode|MUST_EXIST);
  403. X
  404. X    db_sequential = 0;
  405. X    if (mode == OPEN_CREATE) db_sequential = 1;
  406. X
  407. X    if (mode != OPEN_READ) return;
  408. X
  409. X    db_read_master();
  410. X
  411. X    if (who_am_i != I_AM_MASTER && master.db_lock[0]) {
  412. X    printf("DATABASE LOCKED.\n%s\n", master.db_lock);
  413. X    nn_exit(88);
  414. X    }
  415. X
  416. X    freeobj(sorted_groups);
  417. X    freeobj(active_groups);
  418. X    active_groups = NULL;
  419. X    sorted_groups = NULL;
  420. X
  421. X    db_expand_master();
  422. X
  423. X    open_groups(OPEN_READ|MUST_EXIST);
  424. X
  425. X    trust_master = (who_am_i != I_AM_MASTER || !reread_groups_file);
  426. X
  427. X    db_sequential = 1;
  428. X    Loop_Groups_Header(gh) {
  429. X    gh->group_num = l_g_index;
  430. X    db_read_group(gh);
  431. X    db_parse_group(gh, trust_master);
  432. X    }
  433. X    db_sequential = 0;
  434. X
  435. X    close_groups();
  436. X
  437. X    sort_groups();
  438. X}
  439. X
  440. Xdb_expand_master()
  441. X{
  442. X    master.free_groups = 20;
  443. X
  444. X    active_groups = resizeobj(active_groups, group_header,
  445. X                  master.number_of_groups + master.free_groups);
  446. X    sorted_groups = resizeobj(sorted_groups, group_header *,
  447. X                  master.number_of_groups + master.free_groups);
  448. X}
  449. X
  450. Xclose_master()
  451. X{
  452. X    if (master_file != NULL) {
  453. X    fclose(master_file);
  454. X    master_file = NULL;
  455. X    }
  456. X}
  457. X
  458. Xupdate_group(gh)
  459. Xgroup_header *gh;
  460. X{
  461. X    group_number numg;
  462. X
  463. X    numg = master.number_of_groups;
  464. X    db_read_master();
  465. X    master.number_of_groups = numg;
  466. X    if (master.db_lock[0]) return -3;
  467. X
  468. X    db_read_group(gh);
  469. X
  470. X    if (gh->master_flag & M_IGNORE_GROUP) return 0;
  471. X    if (gh->master_flag & M_BLOCKED) return -1;
  472. X
  473. X    return 1;
  474. X}
  475. X
  476. X
  477. Xstatic sort_gh(g1, g2)
  478. Xgroup_header **g1, **g2;
  479. X{
  480. X    return strcmp((*g1)->group_name, (*g2)->group_name);
  481. X}
  482. X
  483. X
  484. Xsort_groups()
  485. X{
  486. X    register group_header *gh;
  487. X
  488. X    Loop_Groups_Header(gh)
  489. X    sorted_groups[l_g_index] = gh;
  490. X
  491. X    quicksort(sorted_groups, master.number_of_groups, group_header *, sort_gh);
  492. X
  493. X    s_g_first = 0;
  494. X    Loop_Groups_Sorted(gh)
  495. X    if (gh->group_name[0] != NUL) {
  496. X        s_g_first = l_g_index;
  497. X        break;
  498. X    }
  499. X}
  500. X
  501. X
  502. Xgroup_header *lookup_no_alias(name)
  503. Xchar *name;
  504. X{
  505. X    register i, j, k, t;
  506. X
  507. X    i = s_g_first; j = master.number_of_groups - 1;
  508. X
  509. X    while (i <= j) {
  510. X    k = (i + j) / 2;
  511. X
  512. X    if ( (t=strcmp(name, sorted_groups[k]->group_name)) > 0)
  513. X        i = k+1;
  514. X    else
  515. X    if (t < 0)
  516. X        j = k-1;
  517. X    else
  518. X        return sorted_groups[k];
  519. X    }
  520. X
  521. X    return NULL;
  522. X}
  523. X
  524. Xgroup_header *lookup(name)
  525. Xchar *name;
  526. X{
  527. X    register group_header *gh;
  528. X
  529. X    gh = lookup_no_alias(name);
  530. X    if (gh == NULL || (gh->master_flag & M_ALIASED) == 0) return gh;
  531. X
  532. X    do
  533. X    gh = &active_groups[gh->data_write_offset];
  534. X    while (gh->master_flag & M_ALIASED);
  535. X
  536. X    return gh;
  537. X}
  538. X
  539. Xart_collected(gh, art_num)
  540. Xgroup_header *gh;
  541. Xarticle_number art_num;
  542. X{
  543. X    return gh->first_db_article <= art_num && gh->last_db_article >= art_num;
  544. X}
  545. X
  546. Xchar *db_data_path(namebuf, gh, d_or_x)
  547. Xchar *namebuf;
  548. Xgroup_header *gh;
  549. Xchar d_or_x;
  550. X{
  551. X    register char *cp, *np;
  552. X
  553. X    if (db_data_directory != NULL) {
  554. X#ifdef DB_LONG_NAMES
  555. X    sprintf(namebuf, "%s/%s.%c", db_data_directory, gh->group_name, d_or_x);
  556. X#else
  557. X    sprintf(namebuf, "%s/%d.%c", db_data_directory, gh->group_num, d_or_x);
  558. X#endif
  559. X    } else {
  560. X    np = namebuf;
  561. X    /* master chdir to the group's directory */
  562. X    if (who_am_i != I_AM_MASTER) {
  563. X        for (cp = news_directory; *np = *cp++; np++);
  564. X        *np++ = '/';
  565. X        for (cp = gh->group_name; *cp; cp++)
  566. X        *np++ = *cp == '.' ? '/' : *cp;
  567. X        *np++ = '/';
  568. X    }
  569. X
  570. X    *np++ = '.';
  571. X    *np++ = 'n';
  572. X    *np++ = 'n';
  573. X    *np++ = d_or_x;
  574. X    *np++ = NUL;
  575. X    }
  576. X
  577. X    return namebuf;
  578. X}
  579. X
  580. X
  581. XFILE *open_data_file(gh, d_or_x, mode)
  582. Xgroup_header *gh;
  583. Xchar d_or_x;
  584. Xint mode;
  585. X{
  586. X    char data_file[FILENAME];
  587. X
  588. X    db_data_path(data_file, gh, d_or_x);
  589. X
  590. X    if (mode == -1) {
  591. X    unlink(data_file);
  592. X    return (FILE *)NULL;
  593. X    } else
  594. X    return open_file(data_file, mode);
  595. X}
  596. X
  597. X
  598. X#ifdef NETWORK_DATABASE
  599. X
  600. X#define MASTER_FIELDS    5    /* + DB_LOCK_MESSAGE bytes */
  601. X#define    GROUP_FIELDS    9
  602. X#define    ARTICLE_FIELDS    10
  603. X
  604. X
  605. Xtypedef int32 net_long;
  606. X
  607. X
  608. X#ifdef NETWORK_BYTE_ORDER
  609. X
  610. X#define net_to_host(buf, n)
  611. X#define host_to_net(buf, n)
  612. X
  613. X#else
  614. X
  615. Xstatic net_to_host(buf, lgt)
  616. Xregister net_long *buf;
  617. Xint lgt;
  618. X{
  619. X    while (--lgt >= 0) {
  620. X    *buf = ntohl(*buf);
  621. X    buf++;
  622. X    }
  623. X}
  624. X
  625. Xstatic host_to_net(buf, lgt)
  626. Xregister net_long *buf;
  627. Xint lgt;
  628. X{
  629. X    while (--lgt >= 0) {
  630. X    *buf = htonl(*buf);
  631. X    buf++;
  632. X    }
  633. X}
  634. X#endif /* not NETWORK_BYTE_ORDER */
  635. X#endif /* NETWORK_DATABASE */
  636. X
  637. X#define NWDB_MAGIC 0x00190000    /* NN#n <-> NW#n */
  638. X
  639. Xdb_read_master()
  640. X{
  641. X#ifdef NETWORK_DATABASE
  642. X    net_long buf[MASTER_FIELDS];
  643. X
  644. X    rewind(master_file);
  645. X    if (fread((char *)buf, sizeof(net_long), MASTER_FIELDS, master_file)
  646. X    != MASTER_FIELDS) goto err;
  647. X    if (fread(master.db_lock, sizeof(char), DB_LOCK_MESSAGE, master_file)
  648. X    != DB_LOCK_MESSAGE) goto err;
  649. X
  650. X    net_to_host(buf, MASTER_FIELDS);
  651. X
  652. X    master.db_magic = buf[0] ^ NWDB_MAGIC;
  653. X    master.last_scan = buf[1];
  654. X    master.last_size = buf[2];
  655. X    master.number_of_groups = buf[3];
  656. X    master.db_created = buf[4];
  657. X
  658. X#else
  659. X    rewind(master_file);
  660. X    if (fread((char *)&master, sizeof(master_header), 1, master_file) != 1)
  661. X    goto err;
  662. X#endif
  663. X
  664. X    if (master.db_magic != NNDB_MAGIC)
  665. X    sys_error("Database magic number mismatch");
  666. X    return;
  667. X
  668. X err:
  669. X    sys_error("Incomplete MASTER file");
  670. X}
  671. X
  672. X
  673. Xdb_write_master()
  674. X{
  675. X#ifdef NETWORK_DATABASE
  676. X    net_long buf[MASTER_FIELDS];
  677. X
  678. X    buf[0] = master.db_magic ^ NWDB_MAGIC;
  679. X    buf[1] = master.last_scan;
  680. X    buf[2] = master.last_size;
  681. X    buf[3] = master.number_of_groups;
  682. X    buf[4] = master.db_created;
  683. X
  684. X    host_to_net(buf, MASTER_FIELDS);
  685. X    rewind(master_file);
  686. X    if (fwrite((char *)buf, sizeof(net_long), MASTER_FIELDS, master_file)
  687. X    != MASTER_FIELDS) goto err;
  688. X    if (fwrite(master.db_lock, sizeof(char), DB_LOCK_MESSAGE, master_file)
  689. X    != DB_LOCK_MESSAGE) goto err;
  690. X#else
  691. X    rewind(master_file);
  692. X    if (fwrite((char *)&master, sizeof(master_header), 1, master_file) != 1)
  693. X    goto err;
  694. X#endif
  695. X
  696. X    fflush(master_file);
  697. X    return;
  698. X
  699. X err:
  700. X    sys_error("Write to MASTER failed");
  701. X}
  702. X
  703. Xdb_read_group(gh)
  704. Xregister group_header *gh;
  705. X{
  706. X#ifdef NETWORK_DATABASE
  707. X    net_long buf[GROUP_FIELDS];
  708. X
  709. X    if (!db_sequential)
  710. X    fseek(master_file,
  711. X          (off_t)(MASTER_FIELDS * sizeof(net_long) + DB_LOCK_MESSAGE +
  712. X          GROUP_FIELDS * sizeof(net_long) * gh->group_num), 0);
  713. X
  714. X    if (fread((char *)buf, sizeof(net_long), GROUP_FIELDS, master_file) != GROUP_FIELDS)
  715. X    goto err;
  716. X
  717. X    net_to_host(buf, GROUP_FIELDS);
  718. X
  719. X    gh->first_db_article = buf[0];
  720. X    gh->last_db_article = buf[1];
  721. X    gh->index_write_offset = buf[2];
  722. X    gh->data_write_offset = buf[3];
  723. X    gh->group_name_length = buf[4];
  724. X    gh->master_flag = buf[5];
  725. X    gh->first_a_article = buf[6];
  726. X    gh->last_a_article = buf[7];
  727. X    gh->creation_time = buf[8];
  728. X#else
  729. X
  730. X    if (!db_sequential)
  731. X    fseek(master_file, 
  732. X          (off_t)(sizeof(master_header) + SAVED_GROUP_HEADER_SIZE(*gh) * gh->group_num), 0);
  733. X
  734. X    if (fread((char *)gh, SAVED_GROUP_HEADER_SIZE(*gh), 1, master_file) != 1)
  735. X    goto err;
  736. X
  737. X#endif
  738. X    return;
  739. X
  740. X err:
  741. X    sys_error("Read GROUPS failed");
  742. X}
  743. X
  744. X
  745. Xdb_write_group(gh)
  746. Xregister group_header *gh;
  747. X{
  748. X#ifdef NETWORK_DATABASE
  749. X    net_long buf[GROUP_FIELDS];
  750. X
  751. X    if (!db_sequential)
  752. X    fseek(master_file,
  753. X          (off_t)(MASTER_FIELDS * sizeof(net_long) + DB_LOCK_MESSAGE +
  754. X          GROUP_FIELDS * sizeof(net_long) * gh->group_num), 0);
  755. X
  756. X    buf[0] = gh->first_db_article;
  757. X    buf[1] = gh->last_db_article;
  758. X    buf[2] = gh->index_write_offset;
  759. X    buf[3] = gh->data_write_offset;
  760. X    buf[4] = gh->group_name_length;
  761. X    buf[5] = gh->master_flag;
  762. X    buf[6] = gh->first_a_article;
  763. X    buf[7] = gh->last_a_article;
  764. X    buf[8] = gh->creation_time;
  765. X
  766. X    host_to_net(buf, GROUP_FIELDS);
  767. X    if (fwrite((char *)buf, sizeof(net_long), GROUP_FIELDS, master_file) != GROUP_FIELDS)
  768. X    goto err;
  769. X#else
  770. X    if (!db_sequential)
  771. X    fseek(master_file, (off_t)(sizeof(master_header) + SAVED_GROUP_HEADER_SIZE(*gh) * gh->group_num), 0);
  772. X
  773. X    if (fwrite((char *)gh, SAVED_GROUP_HEADER_SIZE(*gh), 1, master_file) != 1)
  774. X    goto err;
  775. X#endif
  776. X    fflush(master_file);
  777. X    return;
  778. X
  779. X err:
  780. X    sys_error("Write GROUPS failed");
  781. X}
  782. X
  783. Xdb_read_art(f)
  784. XFILE *f;
  785. X{
  786. X#ifdef NETWORK_DATABASE
  787. X    net_long buf[ARTICLE_FIELDS];
  788. X
  789. X    if (fread((char *)buf, sizeof(net_long), ARTICLE_FIELDS, f) != ARTICLE_FIELDS)
  790. X    return 0;
  791. X
  792. X    net_to_host(buf, ARTICLE_FIELDS);
  793. X
  794. X    db_hdr.dh_number = buf[0];
  795. X    db_hdr.dh_date = buf[1];
  796. X    db_hdr.dh_hpos = buf[2];
  797. X    db_hdr.dh_lpos = buf[3];
  798. X    db_hdr.dh_fpos = buf[4];
  799. X    db_hdr.dh_lines = buf[5];
  800. X    db_hdr.dh_replies = buf[6];
  801. X    db_hdr.dh_cross_postings = buf[7];
  802. X    db_hdr.dh_subject_length = buf[8];
  803. X    db_hdr.dh_sender_length = buf[9];
  804. X#else
  805. X    if (fread((char *)&db_hdr, sizeof(data_header), 1, f) != 1) return 0;
  806. X#endif
  807. X
  808. X    if (db_hdr.dh_number < 0) {
  809. X    current_digest_article = db_hdr.dh_number = -db_hdr.dh_number;
  810. X    db_data.dh_type = DH_DIGEST_HEADER;
  811. X    } else
  812. X    if (db_hdr.dh_number == 0) {
  813. X    db_hdr.dh_number = current_digest_article;
  814. X    db_data.dh_type = DH_SUB_DIGEST;
  815. X    } else {
  816. X    current_digest_article = 0;
  817. X    db_data.dh_type = DH_NORMAL;
  818. X    }
  819. X
  820. X    if (db_hdr.dh_cross_postings)
  821. X        if (fread((char *)db_data.dh_cross, sizeof(cross_post_number),
  822. X          (int)db_hdr.dh_cross_postings, f)
  823. X        != (int)db_hdr.dh_cross_postings) return -1;
  824. X
  825. X    if (db_hdr.dh_sender_length)
  826. X    if (fread(db_data.dh_sender, sizeof(char),
  827. X          (int)db_hdr.dh_sender_length, f)
  828. X        != db_hdr.dh_sender_length) return -1;
  829. X    db_data.dh_sender[db_hdr.dh_sender_length] = NUL;
  830. X
  831. X    if (db_hdr.dh_subject_length)
  832. X    if (fread(db_data.dh_subject, sizeof(char),
  833. X          (int)db_hdr.dh_subject_length, f)
  834. X        !=  db_hdr.dh_subject_length) return -1;
  835. X    db_data.dh_subject[db_hdr.dh_subject_length] = NUL;
  836. X
  837. X    db_read_counter++;
  838. X
  839. X    return 1;
  840. X}
  841. X
  842. Xdb_write_art(f)
  843. XFILE *f;
  844. X{
  845. X#ifdef NETWORK_DATABASE
  846. X    net_long buf[ARTICLE_FIELDS];
  847. X#endif
  848. X    article_number art_num = db_hdr.dh_number;
  849. X
  850. X    switch (db_data.dh_type) {
  851. X     case DH_NORMAL:
  852. X    break;
  853. X     case DH_SUB_DIGEST:
  854. X    db_hdr.dh_number = 0;
  855. X    break;
  856. X     case DH_DIGEST_HEADER:
  857. X    db_hdr.dh_number = -art_num;
  858. X    break;
  859. X    }
  860. X
  861. X#ifdef NETWORK_DATABASE
  862. X    buf[0] = db_hdr.dh_number;
  863. X    buf[1] = db_hdr.dh_date;
  864. X    buf[2] = db_hdr.dh_hpos;
  865. X    buf[3] = db_hdr.dh_lpos;
  866. X    buf[4] = db_hdr.dh_fpos;
  867. X    buf[5] = db_hdr.dh_lines;
  868. X    buf[6] = db_hdr.dh_replies;
  869. X    buf[7] = db_hdr.dh_cross_postings;
  870. X    buf[8] = db_hdr.dh_subject_length;
  871. X    buf[9] = db_hdr.dh_sender_length;
  872. X
  873. X    host_to_net(buf, ARTICLE_FIELDS);
  874. X
  875. X    if (fwrite((char *)buf, sizeof(net_long), ARTICLE_FIELDS, f) != ARTICLE_FIELDS)
  876. X    return -1;
  877. X#else
  878. X
  879. X    if (fwrite((char *)&db_hdr, sizeof(data_header), 1, f) != 1)
  880. X    return -1;
  881. X
  882. X#endif
  883. X    if (db_hdr.dh_cross_postings)
  884. X        if (fwrite((char *)db_data.dh_cross, sizeof(cross_post_number),
  885. X          (int)db_hdr.dh_cross_postings, f)
  886. X        != (int)db_hdr.dh_cross_postings) return -1;
  887. X
  888. X    if (db_hdr.dh_sender_length)
  889. X    if (fwrite(db_data.dh_sender, sizeof(char),
  890. X          (int)db_hdr.dh_sender_length, f)
  891. X        != db_hdr.dh_sender_length) return -1;
  892. X
  893. X    if (db_hdr.dh_subject_length)
  894. X    if (fwrite(db_data.dh_subject, sizeof(char),
  895. X          (int)db_hdr.dh_subject_length, f)
  896. X        != db_hdr.dh_subject_length) return -1;
  897. X
  898. X    db_hdr.dh_number = art_num;
  899. X
  900. X    db_write_counter++;
  901. X
  902. X    return 1;
  903. X}
  904. X
  905. X
  906. Xoff_t get_index_offset(gh, art_num)
  907. Xgroup_header *gh;
  908. Xarticle_number art_num;
  909. X{
  910. X#ifdef NETWORK_DATABASE
  911. X    return (off_t)((art_num - gh->first_db_article) * sizeof(net_long));
  912. X#else
  913. X    return (off_t)((art_num - gh->first_db_article) * sizeof(off_t));
  914. X#endif
  915. X}
  916. X
  917. Xoff_t get_data_offset(gh, art_num)
  918. Xgroup_header *gh;
  919. Xarticle_number art_num;
  920. X{
  921. X    FILE *index;
  922. X    off_t data_offset;
  923. X
  924. X    if (gh->first_db_article == art_num) return (off_t)0;
  925. X
  926. X    index = open_data_file(gh, 'x', OPEN_READ);
  927. X    if (index == NULL) return (off_t)(-1);
  928. X
  929. X    fseek(index, get_index_offset(gh, art_num), 0);
  930. X    if (!db_read_offset(index, &data_offset))
  931. X    return (off_t)(-1);
  932. X
  933. X    fclose(index);
  934. X
  935. X    return data_offset;
  936. X}
  937. X
  938. X
  939. Xdb_read_offset(f, offset)
  940. XFILE *f;
  941. Xoff_t *offset;
  942. X{
  943. X#ifdef NETWORK_DATABASE
  944. X    net_long temp;
  945. X
  946. X    if (fread((char *)&temp, sizeof(net_long), 1, f) != 1) return 0;
  947. X
  948. X#ifndef NETWORK_BYTE_ORDER
  949. X    temp = ntohl(temp);
  950. X#endif
  951. X    *offset = temp;
  952. X#else
  953. X
  954. X    if (fread((char *)offset, sizeof(off_t), 1, f) != 1) return 0;
  955. X#endif
  956. X    return 1;
  957. X}
  958. X
  959. Xdb_write_offset(f, offset)
  960. XFILE *f;
  961. Xoff_t *offset;
  962. X{
  963. X#ifdef NETWORK_DATABASE
  964. X    net_long temp;
  965. X
  966. X    temp = *offset;
  967. X
  968. X#ifndef NETWORK_BYTE_ORDER
  969. X    temp = htonl(temp);
  970. X#endif
  971. X    if (fwrite((char *)&temp, sizeof(net_long), 1, f) != 1) return 0;
  972. X
  973. X#else
  974. X
  975. X    if (fwrite((char *)offset, sizeof(off_t), 1, f) != 1) return 0;
  976. X#endif
  977. X    return 1;
  978. X}
  979. X
  980. END_OF_FILE
  981.   if test 20768 -ne `wc -c <'db.c'`; then
  982.     echo shar: \"'db.c'\" unpacked with wrong size!
  983.   fi
  984.   # end of 'db.c'
  985. fi
  986. if test -f 'folder.c' -a "${1}" != "-c" ; then 
  987.   echo shar: Will not clobber existing file \"'folder.c'\"
  988. else
  989.   echo shar: Extracting \"'folder.c'\" \(8918 characters\)
  990.   sed "s/^X//" >'folder.c' <<'END_OF_FILE'
  991. X/*
  992. X *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  993. X *
  994. X *    Folder handling
  995. X */
  996. X
  997. X/*#include <errno.h>*/
  998. X#include "config.h"
  999. X#include "articles.h"
  1000. X#include "news.h"
  1001. X#include "term.h"
  1002. X#include "menu.h"
  1003. X
  1004. Xexport int  dont_sort_folders = 0;
  1005. Xexport char *folder_directory  = NULL;
  1006. X
  1007. X/*
  1008. X *     file name completion and expansion
  1009. X *
  1010. X *    expand_mode bits:
  1011. X *        1:    expand path names
  1012. X *        2:    don't expand $N
  1013. X *        4:    don't expand any $?  (but $(...) is expanded)
  1014. X *        8:    don't complain about ~... (shell will do that)
  1015. X */
  1016. X
  1017. X
  1018. Xexpand_file_name(dest, src, expand_mode)
  1019. Xchar *dest, *src;
  1020. Xint expand_mode;
  1021. X{
  1022. X    register char *cp, *dp, c;
  1023. X    int parse, remap;
  1024. X    char *cur_grp, *cur_art;
  1025. X    import char *home_directory;
  1026. X
  1027. X    cur_grp = current_group ? current_group->group_name : NULL;
  1028. X    cur_art = (group_file_name && *group_file_name) ? group_path_name : NULL;
  1029. X
  1030. X    for (dp = dest, parse = 1; c = *src; src++) {
  1031. X
  1032. X    if (parse) {
  1033. X
  1034. X        if ((expand_mode & 1) && c == '+') {
  1035. X        if (folder_directory == NULL) {
  1036. X            if (!(cp = getenv("FOLDER")))
  1037. X            cp = FOLDER_DIRECTORY;
  1038. X            folder_directory = home_relative(cp);
  1039. X        }
  1040. X
  1041. X        cp = folder_directory;
  1042. X        goto cp_str;
  1043. X        }
  1044. X
  1045. X        if ((expand_mode & 1) && c == '~') {
  1046. X        if (src[1] != '/') {
  1047. X            if (expand_mode & 8) goto copy;
  1048. X            msg("Can't handle ~user expansion (yet)");
  1049. X            return 0;
  1050. X        }
  1051. X
  1052. X        cp = home_directory;
  1053. X
  1054. X         cp_str:
  1055. X        while (*cp) *dp++ = *cp++;
  1056. X        if (dp[-1] != '/') *dp++ = '/';
  1057. X        goto no_parse;
  1058. X        }
  1059. X
  1060. X        if ((expand_mode & 4) == 0 &&
  1061. X        cur_art && c == '%' && (src[1] == ' ' || src[1] == NUL)) {
  1062. X        cp = cur_art;
  1063. X        while (*cp) *dp++ = *cp++;
  1064. X        goto no_parse;
  1065. X        }
  1066. X
  1067. X    }
  1068. X
  1069. X    if (c == '$' && src[1] == '(') {
  1070. X        char envar[64];
  1071. X        for (src += 2, cp = envar; (c = *src) != NUL && c != ')'; src++)
  1072. X        *cp++ = c;
  1073. X        *cp = NUL;
  1074. X        if (cp != envar) {
  1075. X        if ((cp = getenv(envar)) != NULL)
  1076. X            while (*cp) *dp++ = *cp++;
  1077. X        else {
  1078. X            msg("Environment variable $(%d) not set", envar);
  1079. X            return 0;
  1080. X        }
  1081. X        }
  1082. X        goto no_parse;
  1083. X    }
  1084. X
  1085. X    if ((expand_mode & 4) == 0 && c == '$' && !isalnum(src[2])) {
  1086. X        remap = 0;
  1087. X        cp = NULL;
  1088. X
  1089. X        switch (src[1]) {
  1090. X         case 'A':
  1091. X        cp = cur_art;
  1092. X        break;
  1093. X         case 'F':
  1094. X        cp = cur_grp;
  1095. X        remap = 1;
  1096. X        break;
  1097. X         case 'G':
  1098. X        cp = cur_grp;
  1099. X        break;
  1100. X         case 'L':
  1101. X        if (cp = strrchr(cur_grp, '.'))
  1102. X            cp++;
  1103. X        else
  1104. X            cp = cur_grp;
  1105. X        break;
  1106. X         case 'N':
  1107. X        if (expand_mode & 2) goto copy;
  1108. X        if (cur_art) cp = group_file_name;
  1109. X        if (cp == NULL) goto copy;
  1110. X        break;
  1111. X         default:
  1112. X        goto copy;
  1113. X        }
  1114. X        src++;
  1115. X
  1116. X        if (!cp) {
  1117. X        msg("$%c not defined on this level", c);
  1118. X        return 0;
  1119. X        }
  1120. X
  1121. X        while (*cp)
  1122. X        if (remap && *cp == '.')
  1123. X            cp++, *dp++ = '/';
  1124. X        else
  1125. X            *dp++ = *cp++;
  1126. X        goto no_parse;
  1127. X    }
  1128. X
  1129. X    if (c == '/')
  1130. X        if (dp != dest && dp[-1] == '/') goto no_parse;
  1131. X
  1132. X     copy:
  1133. X    *dp++ = c;
  1134. X    parse = isspace(c);
  1135. X    continue;
  1136. X
  1137. X     no_parse:
  1138. X        parse = 0;
  1139. X    }
  1140. X
  1141. X    *dp = NUL;
  1142. X
  1143. X    return 1;
  1144. X}
  1145. X
  1146. X
  1147. Xfile_completion(path, index)
  1148. Xchar *path;
  1149. Xint index;
  1150. X{
  1151. X    static dir_in_use = 0;
  1152. X    static char *head, *tail = NULL;
  1153. X    static int  tail_offset;
  1154. X
  1155. X    char nbuf[FILENAME], buffer[FILENAME];
  1156. X    char *dir, *base;
  1157. X
  1158. X    if (path) {
  1159. X    if (dir_in_use) {
  1160. X        close_directory();
  1161. X        dir_in_use = 0;
  1162. X    }
  1163. X
  1164. X    if (index < 0) return 0;
  1165. X
  1166. X    head = path;
  1167. X    tail = path + index;
  1168. X    }
  1169. X
  1170. X    if (!dir_in_use) {
  1171. X    path = head;
  1172. X    *tail = NUL;
  1173. X
  1174. X    if (*path == '|') return -1;    /* no completion for pipes */
  1175. X
  1176. X    if (*path == '+' || *path == '~') {
  1177. X        if (!expand_file_name(nbuf, path, 1))
  1178. X        return 0;    /* no completions */
  1179. X    } else
  1180. X        strcpy(nbuf, path);
  1181. X
  1182. X    if (base = strrchr(nbuf, '/')) {
  1183. X        if (base == nbuf) {
  1184. X        dir = "/";
  1185. X        base++;
  1186. X        } else {
  1187. X        *base++ = NUL;
  1188. X        dir = nbuf;
  1189. X        }
  1190. X    } else {
  1191. X        base = nbuf;
  1192. X        dir = ".";
  1193. X    }
  1194. X
  1195. X    tail_offset = strlen(base);
  1196. X
  1197. X    dir_in_use = list_directory(dir, base);
  1198. X
  1199. X    return dir_in_use;
  1200. X    }
  1201. X
  1202. X    if (index)
  1203. X    return compl_help_directory();
  1204. X
  1205. X    if (!next_directory(buffer, 1)) return 0;
  1206. X
  1207. X    strcpy(tail, buffer+tail_offset);
  1208. X
  1209. X    return 1;
  1210. X}
  1211. X
  1212. X
  1213. Xstatic int cancel_count;
  1214. X
  1215. Xfcancel(ah)
  1216. Xarticle_header *ah;
  1217. X{
  1218. X    if (ah->attr == A_CANCEL) {
  1219. X    cancel_count--;
  1220. X    ah->flag = 0;
  1221. X    } else {
  1222. X    cancel_count++;
  1223. X    ah->attr = A_CANCEL;
  1224. X    }
  1225. X}
  1226. X
  1227. Xstatic folder_header()
  1228. X{
  1229. X    so_printxy(0, 0, "Folder: %s", current_group->group_name);
  1230. X
  1231. X    return 1;    /* number of header lines */
  1232. X}
  1233. X
  1234. Xfolder_menu(path)
  1235. Xchar *path;
  1236. X{
  1237. X    FILE             *folder;
  1238. X    register article_header    *ah;
  1239. X    news_header_buffer         dgbuf;
  1240. X    char             buffer[256];
  1241. X    int                more, length, re, menu_cmd, was_raw;
  1242. X    memory_marker        mem_marker;
  1243. X    group_header         fake_group;
  1244. X    int                cc_save;
  1245. X    extern time_stamp pack_date();
  1246. X
  1247. X    fake_group.group_name = path;
  1248. X    fake_group.group_flag = G_FOLDER | G_FAKED;
  1249. X    fake_group.master_flag = 0;
  1250. X    fake_group.save_file = NULL;
  1251. X    current_group = NULL;
  1252. X    init_group(&fake_group);
  1253. X
  1254. X    folder = open_file(path, OPEN_READ);
  1255. X    if (folder == NULL) {
  1256. X    msg("%s not found", path);
  1257. X    return ME_NO_REDRAW;
  1258. X    }
  1259. X
  1260. X    was_raw = no_raw();
  1261. X    s_keyboard = 0;
  1262. X
  1263. X    printf("\rReading: %-.65s", path);
  1264. X    clrline();
  1265. X
  1266. X    current_group = &fake_group;
  1267. X
  1268. X    mark_memory(&mem_marker);
  1269. X
  1270. X    ah = alloc_art();
  1271. X
  1272. X    more = 1;
  1273. X    while (more && (more = get_digest_article(folder, dgbuf)) >= 0) {
  1274. X    if (s_keyboard) break;
  1275. X
  1276. X    ah->a_number = 0;
  1277. X    ah->flag = A_FOLDER;
  1278. X    ah->attr = 0;
  1279. X
  1280. X    ah->lines = digest.dg_lines;
  1281. X
  1282. X    ah->hpos = digest.dg_hpos;
  1283. X    ah->fpos = digest.dg_fpos;
  1284. X    ah->lpos = digest.dg_lpos;
  1285. X
  1286. X    if (digest.dg_from) {
  1287. X        length = pack_name(buffer, digest.dg_from, NAME_LENGTH);
  1288. X        ah->sender = alloc_str(length);
  1289. X        strcpy(ah->sender, buffer);
  1290. X        ah->name_length = length;
  1291. X    } else {
  1292. X        ah->sender = "";
  1293. X        ah->name_length = 0;
  1294. X    }
  1295. X
  1296. X    if (digest.dg_subj) {
  1297. X        length = pack_subject(buffer, digest.dg_subj, &re, 255);
  1298. X        ah->replies = re;
  1299. X        ah->subject = alloc_str(length);
  1300. X        strcpy(ah->subject, buffer);
  1301. X        ah->subj_length = length;
  1302. X    } else {
  1303. X        ah->replies = 0;
  1304. X        ah->subject = "";
  1305. X        ah->subj_length = 0;
  1306. X    }
  1307. X
  1308. X    ah->t_stamp = digest.dg_date ? pack_date(digest.dg_date) : 0;
  1309. X
  1310. X    add_article(ah);
  1311. X    ah = alloc_art();
  1312. X    }
  1313. X
  1314. X    fclose(folder);
  1315. X
  1316. X    if (was_raw) raw();
  1317. X
  1318. X    if (s_keyboard) {
  1319. X    menu_cmd = ME_NO_REDRAW;
  1320. X    } else
  1321. X    if (n_articles == 0) {
  1322. X    msg("Not a folder (no article header)");
  1323. X    menu_cmd = ME_NO_REDRAW;
  1324. X    } else {
  1325. X    strcpy(buffer, path);
  1326. X    fake_group.group_name = buffer;    /* save for later use */
  1327. X
  1328. X    if (n_articles > 1) {
  1329. X        clrdisp();
  1330. X        prompt_line = 2;
  1331. X        if (!dont_sort_folders) sort_articles(-1);
  1332. X    }
  1333. X
  1334. X    cc_save = cancel_count;
  1335. X    cancel_count = 0;
  1336. X
  1337. X     reenter_menu:
  1338. X    menu_cmd = menu(folder_header);
  1339. X
  1340. X    if (cancel_count) {
  1341. X        clrdisp();
  1342. X        printf("Folder: %s\nFile:   %s\n\n", buffer, group_path_name);
  1343. X        if (cancel_count == n_articles)
  1344. X        printf("Cancel all articles and remove folder? ");
  1345. X        else
  1346. X        printf("Remove %d article%s from folder? ",
  1347. X               cancel_count, plural((long)cancel_count));
  1348. X        fl;
  1349. X
  1350. X        switch (yes(1)) {
  1351. X         case 1:
  1352. X        printf("\n\n");
  1353. X        if (cancel_count == n_articles) {
  1354. X            if (unlink(group_path_name) < 0) {
  1355. X            printf("Could not unlink %s\n", group_path_name);
  1356. X            sleep(3);
  1357. X            }
  1358. X        } else
  1359. X            rewrite_folder();
  1360. X        break;
  1361. X         case 0:
  1362. X        break;
  1363. X         default:
  1364. X        goto reenter_menu;
  1365. X        }
  1366. X    }
  1367. X    cancel_count = cc_save;
  1368. X    }
  1369. X
  1370. X    release_memory(&mem_marker);
  1371. X
  1372. X    return menu_cmd;
  1373. X}
  1374. X
  1375. X
  1376. Xrewrite_folder()
  1377. X{
  1378. X    register FILE *src, *dst;
  1379. X    char oldfile[FILENAME], *sp;
  1380. X    register int c;
  1381. X    register long cnt;
  1382. X    register article_header *ah, **ahp;
  1383. X    register article_number n;
  1384. X
  1385. X    if ((src = fopen(group_path_name, "r")) == NULL) {
  1386. X    msg("Cannot open %s", group_path_name);
  1387. X    return;
  1388. X    }
  1389. X
  1390. X    strcpy(oldfile, group_path_name);
  1391. X    sp = strrchr(oldfile, '/');
  1392. X    strcpy((sp == NULL ? oldfile : sp+1), "~OLD~FOLDER~");
  1393. X
  1394. X    unlink(oldfile);
  1395. X    if (link(group_path_name, oldfile) < 0) goto move_error;
  1396. X    if (unlink(group_path_name) < 0) {
  1397. X    if (unlink(oldfile) == 0) goto move_error;
  1398. X    printf("\n\n%s was linked to %s --- cannot proceed\n",
  1399. X           group_path_name, oldfile);
  1400. X    sleep(5);
  1401. X    return;
  1402. X    }
  1403. X
  1404. X    if ((dst = fopen(group_path_name, "w")) == NULL) {
  1405. X    fclose(src);
  1406. X    goto move_back;
  1407. X    }
  1408. X
  1409. X    sort_articles(0);
  1410. X
  1411. X    printf("Compressing folder..."); fl;
  1412. X
  1413. X    for (ahp = articles, n = n_articles; --n >= 0; ahp++) {
  1414. X    ah = *ahp;
  1415. X    if (ah->attr == A_CANCEL) continue;
  1416. X    fseek(src, ah->hpos, 0);
  1417. X    cnt = ah->lpos - ah->hpos;
  1418. X    while (--cnt >= 0) {
  1419. X        if ((c = getc(src)) == EOF) break;
  1420. X        putc(c, dst);
  1421. X    }
  1422. X    putc(NL, dst);
  1423. X    }
  1424. X    fclose(src);
  1425. X    if (ferror(dst)) {
  1426. X    fclose(dst);
  1427. X    goto move_back;
  1428. X    }
  1429. X    return;
  1430. X
  1431. Xmove_back:
  1432. X    if (link(oldfile, group_path_name) == 0) {
  1433. X    unlink(oldfile);
  1434. X    printf("Cannot create new file -- Folder restored\n");
  1435. X    sleep(2);
  1436. X    } else {
  1437. X    printf("Cannot create new file\n\nFolder saved in %s\n",
  1438. X           oldfile);
  1439. X    sleep(10);
  1440. X    }
  1441. X    return;
  1442. X
  1443. Xmove_error:
  1444. X    fclose(src);
  1445. X    printf("\n\nCannot move folder %s to %s\n",
  1446. X       group_path_name, oldfile);
  1447. X    sleep(3);
  1448. X    return;
  1449. X}
  1450. END_OF_FILE
  1451.   if test 8918 -ne `wc -c <'folder.c'`; then
  1452.     echo shar: \"'folder.c'\" unpacked with wrong size!
  1453.   fi
  1454.   # end of 'folder.c'
  1455. fi
  1456. if test -f 'man/nnmaster.8' -a "${1}" != "-c" ; then 
  1457.   echo shar: Will not clobber existing file \"'man/nnmaster.8'\"
  1458. else
  1459.   echo shar: Extracting \"'man/nnmaster.8'\" \(21325 characters\)
  1460.   sed "s/^X//" >'man/nnmaster.8' <<'END_OF_FILE'
  1461. X.TH NNMASTER 1M "Release 6.4"
  1462. X.\" (c) Copyright 1988, 1990, Kim F. Storm.  All rights reserved.
  1463. X.UC 4
  1464. X.SH NAME
  1465. Xnnmaster \- nn database manager
  1466. X.SH SYNOPSIS
  1467. X\fBnnmaster\fP \-\fBI\fP [\fIlmit\fP]
  1468. X.br
  1469. X\fBnnmaster\fP \-\fBw\fP
  1470. X.br
  1471. X\fBnnmaster\fP \-\fBv\fP
  1472. X.br
  1473. X\fBnnmaster\fP \-\fBl\fP [ "\fIlock message\fP" ]
  1474. X.br
  1475. X\fBnnmaster\fP [ \fIoptions\fP ] [ \fIgroups\fP ]
  1476. X.br.
  1477. X\fBnnmaster\fP \-\fBF\fP [ \fIoptions\fP ] [ \fIgroups\fP ]
  1478. X.SH DESCRIPTION
  1479. X.I nnmaster
  1480. Xis the daemon which is responsible for building and maintaining the
  1481. Xdatabase used by the \fInn\fP(1) news reader.
  1482. X.LP
  1483. XNormally,
  1484. X.I nnmaster
  1485. Xis started when the system enters multi-user mode, and runs until
  1486. Xsystem shutdown.  To facilitate this, you should place the following
  1487. Xcall in /etc/rc (or similar) to invoke the
  1488. X.I nnmaster
  1489. Xdaemon:
  1490. X.sp 0.5v
  1491. X    $master/nnmaster -l -r -C
  1492. X.sp 0.5v
  1493. Xwhere $master is the MASTER_DIRECTORY defined during configuration
  1494. Xof \fInn\fP.
  1495. X.LP
  1496. XWhen
  1497. X.I nnmaster
  1498. Xis started as specified above, it will first unlock the database in
  1499. Xcase it was locked (\-\fBl\fP), perform a thorough consistency check on the
  1500. Xdatabase (\-\fBC\fP).
  1501. X.LP
  1502. XThen, every 10 minutes (\-\fBr\fP), it will look at the time-stamp
  1503. Xof the news active file to see whether new articles have arrived on
  1504. Xthe system (or whether articles have been expired).
  1505. X.LP
  1506. XIf the active file has been modified,
  1507. X.I nnmaster
  1508. Xwill collect the header information from the new articles and enter
  1509. Xthem into the database (or remove the headers of the expired articles
  1510. Xfrom the database).
  1511. X.LP
  1512. XIf it detects that some articles have been expired, it will
  1513. Xautomatically remove the header information of the expired articles
  1514. Xfrom the database.
  1515. X.SH ARTICLE COLLECTION OPTIONS
  1516. XNormally, \fInnmaster\fP will collect all available news groups
  1517. Xdefined in the news active file.  The set of collected groups can be
  1518. Xcontrolled via the argument line.  Groups can be either included or
  1519. Xexcluded:
  1520. X.sp 0.5v
  1521. XA group name, e.g. comp, will cause the group and all its subgroups to
  1522. Xbe collected.  Individual groups, e.g. news.software.nn, can also be
  1523. Xspecified
  1524. X.sp 0.5v
  1525. XA group name preceeded by an exclamation mark, e.g. !talk.politics,
  1526. Xwill cause the group and all its subgroups to be ignored.
  1527. X.sp 0.5v
  1528. XAn empty argument, i.e. "", will cause all groups that are not ignored
  1529. Xto be collected.  For example, to collect everything but rec and misc,
  1530. Xuse the following command:
  1531. X.br
  1532. X    \fInnmaster\fP -r !rec !misc ""
  1533. X.br
  1534. XIf the empty argument had been omitted, nothing would be collected,
  1535. Xsince the presence of any \fIgroups\fP arguments causes \fInnmaster\fP
  1536. Xto ignore all groups which are not explicitly mentionned.
  1537. X.sp 0.5v
  1538. XExample 1: The following commands can be executed by cron to collect
  1539. Xdifferent sets of groups at different intervals or under different
  1540. Xconditions:
  1541. X.br
  1542. X    \fInnmaster\fP -B -O14 rec misc sci -LBO -u
  1543. X.br
  1544. X    \fInnmaster\fP !rec !misc !sci "" -u
  1545. X.sp 0.5v
  1546. XExample 2: The group arguments are used in the given sequence, e.g. to
  1547. Xleave out comp.sys, but still collect comp.sys.ibm.pc, use the command:
  1548. X.br
  1549. X    \fInnmaster\fP -r comp.sys.ibm.pc !comp.sys ""
  1550. X.sp 0.5v
  1551. XThe use of the \-\fBu\fP option in the first example is essential,
  1552. Xsince each of the commands will update the active file time stamp
  1553. Xwhich will prevent the other command from detecting new articles that
  1554. Xhave arrived.
  1555. X.sp 0.5v
  1556. XUsing this method to keep specific groups out of the database must be
  1557. Xused with great caution; just a single invocation of \fInnmaster\fP
  1558. Xwithout any arguments will collect all the otherwise ignored groups!
  1559. X.SH COLLECTION OF ARTICLES
  1560. XThe following options control how \fInnmaster\fP performs the
  1561. Xcollection of new articles.
  1562. X.TP
  1563. X\-\fBr\fP [ \fImin\fP ]
  1564. X.br
  1565. XDaemon mode.  The \fInnmaster\fP will put itself in the background
  1566. X(unless \-\fBf\fP is also specified),
  1567. Xand will checks for arrival of new articles and expired articles every
  1568. X.I min
  1569. Xminutes (and update the database accordingly).  If
  1570. X.I min
  1571. Xis omitted, the default is to check every 10 minutes.
  1572. X.sp 0.5v
  1573. XWithout the \-r option, the \fInnmaster\fP will just perform a single
  1574. Xcollection of new articles (if any) and then exit.  This can be used
  1575. Xto have the \fInnmaster\fP started by
  1576. X.IR cron (8)
  1577. Xat regular intervals instead of having it as a daemon which sleeps
  1578. Xbetween checking for new articles.  Since the \fInnmaster\fP is a bit
  1579. Xexpensive to start up (it has to read a few files), it is up to you to
  1580. Xdecide which mode is best on your system.  (I have also heard that it
  1581. Xworks to call \fInnmaster\fP without \-r from
  1582. X.IR inews (1).
  1583. XI cannot recommend this unless you receive batched news; invoking
  1584. X\fInnmaster\fP for every received article sounds too expensive to me.)
  1585. X.TP
  1586. X.B \-f
  1587. XRun \fInnmaster\fP in foreground in daemon mode (see \-\fBr\fP).
  1588. XUseful if \fInnmaster\fP is invoked from inittab.  (Notice that if you
  1589. Xuse a \fIrespawn\fP entry in inittab, you will not be able to stop
  1590. X\fInnmaster\fP using the \-\fBk\fP option, since init will immediately
  1591. Xstart another master.)
  1592. X.TP
  1593. X.B \-C
  1594. XPerform a consistency check on the database on start-up, and rebuild
  1595. Xcorrupted database files.  This operation can be quite time-consuming
  1596. Xsince it reads through all the database files.
  1597. X.TP
  1598. X.B \-b
  1599. XNormally, articles without a proper news header (no Newsgroups: line)
  1600. Xare ignored.  Specifying the \-\fBb\fP option causes these `bad'
  1601. Xarticles to be included in the database (normally with no sender or
  1602. Xsubject).
  1603. X.TP
  1604. X.B \-B
  1605. XRemove `bad' articles.  Sometimes, articles without a header ends up
  1606. Xin the news spool directory.  These articles have no article id, and
  1607. Xtherefore, they will never be expired by \fBexpire\fP(8).  This option
  1608. Xwill allow the \fInnmaster\fP to silently remove these articles (a `B'
  1609. Xentry will occur in the log file).
  1610. X.TP
  1611. X\-\fBO\fP \fIdays\fP
  1612. XIgnore articles which are older than the given number of \fIdays\fP.
  1613. XThis may help keep old 'stray' articles out of the database.  If the
  1614. X\-\fBB\fP options is also specified, the old articles will be removed
  1615. Xfrom the news spool directories.  Old ignored or removed articles will
  1616. Xbe reported with an `O' entry in the log file.  This option can be
  1617. Xdisable for individual groups by the \fBO\fP flag in the GROUPS file
  1618. X(see below).
  1619. X.TP
  1620. X\-\fBR\fP \fIN\fP
  1621. XSpecifies how the \fIauto-recollect\fP operation is performed on the
  1622. Xgroups having this option set in the GROUPS file (see below).  Four
  1623. Xmethods are available (default is method 1):
  1624. X.br
  1625. X\fB1\fP:  Run expire on the group when new articles arrive.
  1626. X.br
  1627. X\fB2\fP:  Run expire on the group on every scan.
  1628. X.br
  1629. X\fB3\fP:  Recollect all articles when new articles arrive.
  1630. X.br
  1631. X\fB4\fP:  Recollect all articles on every scan.
  1632. X.br
  1633. X.TP
  1634. X\-\fBQ\fP
  1635. XNormally, \fInnmaster\fP will print a message on the system console or
  1636. Xin the syslog if a fatal error happens.  This option will prevent
  1637. Xthis, so only a type 'E' entry is written to the Log file.
  1638. X.SH DATABASE EXPIRATION
  1639. XSince articles does not stay forever in the news system, the database
  1640. Xmust be cleaned up regularly to remove the information stored for
  1641. Xexpired articles.  Expiration of the database is normally \fBscheduled\fP
  1642. Xusing the \fInnadmin\fP(1M) command executed by cron at a suitable
  1643. Xtime when expire on the news articles has completed.  The following
  1644. Xcommand will send a message to the \fInnmaster\fP and cause it to
  1645. Xinitiate expire on all news groups:
  1646. X.sp 0.5v
  1647. X    nnadmin =EYW
  1648. X.LP
  1649. XSelective expiration of individual groups can be done from
  1650. X\fInnadmin\fP (interactive mode).  It can also be done by invoking
  1651. X\fInnmaster\fP with the \-\fBF\fP option.  For example, the following
  1652. Xcommand will run expire on all groups except the `rec' groups:
  1653. X.sp 0.5v
  1654. X    nnmaster \-F \-k !rec ""
  1655. X.sp 0.5v
  1656. XThe \-\fBk\fP option is required to terminate the currently running
  1657. Xmaster since two daemons cannot be running at the same time.  Thus to
  1658. Xrun expire (on all groups) in this way from cron, the following
  1659. Xcommands must be used:
  1660. X    nnmaster \-Fk "" ; nnmaster \-r ...
  1661. X.LP
  1662. XIt is also possible to have \fInnmaster\fP detect expiration
  1663. Xautomatically (see \-\fBe\fP).  This requires that the \fImin\fP field
  1664. Xin the active file is updated by
  1665. Xthe news expire (this is not the default case when Cnews is used).
  1666. XHowever, this is not always a safe indication since the first article
  1667. Xmay not have been expired, while a lot of other articles have been
  1668. Xdeleted.
  1669. X.LP
  1670. XThere are several strategies available in the
  1671. X\fInnmaster\fP to do this clean-up, each having their strengths and
  1672. Xweaknesses.
  1673. X.LP
  1674. X\fBMethod 1\fP (default):  Rebuilds the database from the existing
  1675. Xdatabase information by comparing the current database contents with
  1676. Xthe contents of the news group directories, eliminating entries whose
  1677. Xfile no longer exists.  This method is guaranteed to eliminate all
  1678. Xexpired articles from the database, and it is reasonably fast because
  1679. Xit only has to read the directories, not each article file.
  1680. X  If news is accessed remotely via nntp, the list of existing articles
  1681. Xcannot efficiently be fetched by reading a local directory.  Instead
  1682. Xan XHDR request is sent to the nntp server to get a list of articles.
  1683. X.LP
  1684. X\fBMethod 2\fP:  Eliminates only the expired articles before the first
  1685. Xarticle in the group.  This is very fast since only the active file
  1686. Xand the database itself is accessed, but it will obviously leave some
  1687. Xexpired articles in the database.  This method requires that the \fImin\fP
  1688. Xfield in the active file is updated by \fIexpire\fP.
  1689. X.LP
  1690. X\fBMethod 3\fP:  Discard the current database information and
  1691. Xrecollects all articles.  This is obviously very time consuming, and
  1692. Xit is therefore not recommended, especially not with nntp.
  1693. X.LP
  1694. XThe options related to database expiration are:
  1695. X.TP
  1696. X\-\fBE\fP \fIN\fP
  1697. XSelect expire method \fIN\fP.  (If \fIN\fP is omitted, the default
  1698. Xmethod is used).
  1699. X.TP
  1700. X\-\fBe\fP [\fIN\fP]
  1701. XAutomatically run expire in the database on groups where the \fImin\fP
  1702. Xnumber in the active file has increased by \fIN\fP (1 if omitted) articles.
  1703. XThis is disabled by default (since the \fImin\fP field is often unreliable).
  1704. X.TP
  1705. X\-\fBF\fP
  1706. XRun expire once and exit.  If a list of \fIgroups\fP is specified on
  1707. Xthe command line, the matched groups (see the rules above) will be
  1708. Xmarked for expiration.  If no groups are specified, only the groups
  1709. Xalready scheduled for expire will be expired.  Consequently, to expire
  1710. Xall groups, a blank argument "" (matching all groups) must be specified.
  1711. X.SH DATABASE LOCKING
  1712. XThe database can be locked centrally, which will normally disallow all
  1713. Xaccess to the database, and even block \fInnmaster\fP from being
  1714. X(accidentally) started.  When a lock is set on the database, all
  1715. Xcurrently running clients will terminate the next time they try to
  1716. Xaccess the database.  Setting a lock on the database can thus also be
  1717. Xused to force running clients to terminate.
  1718. X.LP
  1719. XThe following options set and clear locks on the database:
  1720. X.TP
  1721. X\-\fBl\fP \fImessage\fP
  1722. XLocks the database with the given \fImessage\fP.  The message will be
  1723. Xdisplayed every time a program tries to access the database.
  1724. X.TP
  1725. X.B \-l
  1726. XUnlock the database if it was locked.
  1727. X.TP
  1728. X.B \-i
  1729. XIgnore a possible lock and continue.  This can be used to have
  1730. X\fInnmaster\fP operate on a database which is blocked from normal user
  1731. Xaccess.
  1732. X.LP
  1733. XSince only one \fInnmaster\fP can operate on the database at any one
  1734. Xtime, a running \fInnmaster\fP daemon must be stopped before a lock
  1735. Xcan be set on the database.  If neither
  1736. X.B \-f
  1737. Xnor
  1738. X.B \-r
  1739. Xis specified with the
  1740. X.B \-l
  1741. Xoption (in both forms), \fInnmaster\fP will terminate after setting or
  1742. Xclearing the lock.
  1743. X.SH DATABASE INITIALIZATION
  1744. XThe following options are used to initialize and update the central
  1745. Xdatabase files:
  1746. X.TP
  1747. X\fB\-I\fP [\fIlimit\fP]
  1748. XInitialize database.  This option will erase an existing database, and
  1749. Xcreate an empty database containing entries for the currently known
  1750. Xgroups.  \fInnmaster\fP will offer you to use an existing GROUPS file
  1751. Xwhen initializing the database.
  1752. X.sp 0.5v
  1753. XThe optional \fIlimit\fP can be used to put a limit on the number of
  1754. Xarticles that will be collected in each group during the first
  1755. Xcollection of articles following the database initialization.  This is
  1756. Xuseful on systems where the 'min' field in the active file is
  1757. Xunreliable or not maintained (Cnews doesn't) to limit the work done to
  1758. Xdo the initial collection of news after the initalization of the
  1759. Xdatabase.  If news is accessed remotely from an NNTP server, this is
  1760. Xeven more important!  If \fIlimit\fP is omitted, or is zero,
  1761. X\fInnmaster\fP will trust the min field and collect all articles in
  1762. Xthe range min..last.
  1763. X.TP
  1764. X.B \-G
  1765. XReread the GROUPS file.  This option is used to instruct
  1766. X\fInnmaster\fP to parse the GROUPS file after it has been edited.
  1767. XSee the section on the GROUPS file below.
  1768. X.SH MISCELLANEOUS OPTIONS
  1769. XThe following options controls various details of the \fInnmaster\fP's
  1770. Xbehaviour:
  1771. X.TP
  1772. X\fB\-D\fP [ \fIN\fP ]
  1773. XRun \fInnmaster\fP in "debug mode".  If \fIN\fP is omitted, or equals 1 or
  1774. X3, this will produce a compact but still very informative trace of the
  1775. Xcollection or expire process directly on the terminal.  This is most
  1776. Xuseful when doing the first collection of articles after initializing
  1777. Xthe database with -I.  If \fIN\fP is 2 or 3, a trace of the NNTP
  1778. Xtraffic is written to a file nnmaster.log in the TMP directory. This
  1779. Xoption disables \-\fBr\fP.
  1780. X.TP
  1781. X\-\fBH\fP
  1782. XIdentifies the host which \fInnmaster\fP is running on as the
  1783. Xnntp-server for its clients, i.e. that it can access the news spool
  1784. Xdirectory locally without using NNTP.  Normally, \fInnmaster\fP will
  1785. Xdetect this by itself by comparing the host name to the contents of
  1786. Xthe nntp_server file, so this option should really be superfluous.
  1787. X.TP
  1788. X\-\fBy\fP \fIretries\fP
  1789. X.br
  1790. XIn some networked environment, opening an article (shared from another
  1791. Xmachine via NFS) may fail for no obvious reason.  Using this option,
  1792. Xit is possible to cause
  1793. X\fInnmaster\fP to perform \fIretries\fP attempts to open an article
  1794. Xbefore marking the article as non-existing in the database.
  1795. X.TP
  1796. X\-\fBL\fP \fItypes\fP
  1797. XExclude the specified entry types from the log file.  This is normally
  1798. Xused to exclude the 'C'ollecting and e'X'pire entries (\-\fBL\fPCXO).
  1799. X.TP
  1800. X.B \-t
  1801. XTrace the collection process.  This will place a lot of information
  1802. Xinto the log file (T: entries).
  1803. X.TP
  1804. X.B \-u
  1805. XNormally, \fInnmaster\fP will just compare the time-stamp on the
  1806. Xactive file with a time-stamp saved in the database to see if new
  1807. Xarticles have arrived.  The \-u option forces the \fInnmaster\fP to
  1808. X.I read
  1809. Xthe active file on start-up to see if new articles have arrived.
  1810. X.TP
  1811. X.B \-v
  1812. XPrint the release and version identification for \fInnmaster\fP, and exit.
  1813. X.TP
  1814. X.B \-w
  1815. XWakeup the real \fInnmaster\fP.  Send a signal to the \fInnmaster\fP
  1816. Xdaemon to have it check for new articles immediately.
  1817. X.TP
  1818. X.B \-k
  1819. XKill the already running \fInnmaster\fP daemon before proceeding with
  1820. Xthe operation specified by the other options (or terminate if no other
  1821. Xoperation is requested).
  1822. X.SH THE GROUPS FILE
  1823. XThe primary purpose of the GROUPS file is to store the names of the
  1824. Xnews groups represented in the database.  Each line in the file
  1825. Xcorresponds to an entry in the (binary) MASTER file, and the sequence
  1826. Xof the lines in the GROUPS file must never be changed unless the
  1827. Xdatabase is reinitialized afterwards.
  1828. X.LP
  1829. XHowever, the contents of the lines in the GROUPS file can be edited to
  1830. Xcontrol how the \fInnmaster\fP should handle each individual group.
  1831. X.LP
  1832. XThe format of each line is:
  1833. X.sp 0.5v
  1834. X    news.group.name [ timestamp ] [ flags ]
  1835. X.LP
  1836. XThe news group name is the name of the group, and must not be changed
  1837. Xin any way.  If the group is no longer in the news active file, and
  1838. Xconsequently the group does no longer exist, group name can be
  1839. Xreplaced by a `@' character which will instruct \fInnmaster\fP to
  1840. Xignore this entry without having to rebuild the entire database.
  1841. X.LP
  1842. XThe optional time stamp indicates when the line was added to the
  1843. XGROUPS file and is used by \fInn\fP to detect new groups.  When the
  1844. XGROUPS file is built initially from the active file, the time stamps
  1845. Xare omitted which simply means that they are "old".
  1846. X.LP
  1847. XOne or more of the following flags can be added to the GROUPS line to
  1848. Xcontrol \fInnmaster\fP's handling of the group:
  1849. X.TP
  1850. X.B D
  1851. XCauses \fInnmaster\fP to treat all articles in the group as digests,
  1852. Xeven when they don't initially look like digests.  Articles which are
  1853. Xfound not to be digests after all, are still not digested.
  1854. X.TP
  1855. X.B N
  1856. XInstructs \fInnmaster\fP to never digest any articles in the group.
  1857. X.TP
  1858. X.B O
  1859. XDisables the \-\fBO\fP option for this group, i.e. all existing
  1860. Xarticles will be included in the database (and they will not be
  1861. Xremoved if the \-\fBB\fP option is specified).  This flag should be
  1862. Xset on groups which you never expire, or have a very long expire time!
  1863. X.TP
  1864. X.B R
  1865. XCauses \fInnmaster\fP to \fIrecollect all\fP available articles in the
  1866. Xgroup whenever a new article is received.  This is said to be useful
  1867. Xis some high-traffic clarinet groups with many cancelled articles.
  1868. X.TP
  1869. X\fB>\fP\fIfile\fP
  1870. XInstructs \fInnmaster\fP to \fIappend\fP all new articles to the
  1871. Xspecified file.  This makes it possible to keep specific groups out of
  1872. Xthe way of expire.  The archive file can be access directly from the
  1873. X\fInn\fP client using the \fIgoto-group\fP command.  The file name
  1874. Xmust be a full path name to a file in an existing, writeable directory.
  1875. X.TP
  1876. X.B @
  1877. XInstructs \fInnmaster\fP to completely ignore this group - this is
  1878. Xequivalent to setting the group name to `@' as described above.
  1879. X.TP
  1880. X\fB!\fP or \fBX\fP
  1881. XCauses \fInnmaster\fP to ignore the group and not collect the group's
  1882. Xarticles in the database.
  1883. X.LP
  1884. XComments (starting with `#' and continuing to the end of line) and
  1885. Xempty lines are allow in the GROUPS file, but it is strongly
  1886. Xrecommended to keep the changes to the GROUPS file as small as
  1887. Xpossible, because of the implicit correspondance with the binary
  1888. XMASTER file.
  1889. X.LP
  1890. XIt is not recommended to edit the GROUPS file while \fInnmaster\fP is
  1891. Xrunning because it may add new groups to the file.  After editing the
  1892. XGROUPS file, the command
  1893. X.br
  1894. X    \fInnmaster\fP -G
  1895. X.br
  1896. Xmust be run before restarting the \fInnmaster\fP to parse and verify
  1897. Xthe new GROUPS file.
  1898. X.SH NNTP SUPPORT
  1899. XThe \fInnmaster\fP can access the news articles from a local news
  1900. Xspool directory as well as from an NNTP server.  When compiled with
  1901. XNNTP enabled, \fInnmaster\fP will compare the name of the NNTP server
  1902. Xand the name of the local host; if they are identical, \fInnmaster\fP
  1903. Xwill bypass NNTP and access the articles directly.
  1904. X.LP
  1905. XWhen it has to access the news articles via NNTP, it cannot time-stamp
  1906. Xthe active file, so instead it transfers the entire active file from
  1907. Xthe NNTP server and compares it with a local copy of the last active
  1908. Xfile fetched from the NNTP server.  This is not very expensive in
  1909. Xterms of cpu-cycles, disk-load, or net-bandwidth, but to stay on
  1910. Xfriendly terms with the NNTP server administrator, you should probably
  1911. Xnot use shorter update intervals than the standard 10 minutes.
  1912. X.LP
  1913. XSetting a much higher update interval than the standard 10 minutes is
  1914. Xnot really recommended either, since an update normally implies
  1915. Xfetching a burst of news articles from the NNTP server, so setting the
  1916. Xinterval too long may imply that the load on the NNTP server will be
  1917. Xmore un-even.
  1918. X.LP
  1919. XIn expire method 1, the use of XHDR just to get a list of existing
  1920. Xarticles in a group is definitely a waste of resources on the nntp
  1921. Xserver (but still lower than using method 3).  Before using
  1922. Xthe XHDR request, \fInnmaster\fP will send a
  1923. Xnon-standard "LISTGROUP" request; if the nntp server supports this
  1924. Xrequest, it should return an OK_HEAD status followed by an (unordered)
  1925. Xlist of article numbers (one per line) terminated by a `.' line.  The
  1926. Xnntp servers supporting this request will be much less loaded during
  1927. Xexpire.
  1928. X.LP
  1929. XThe \-\fBO\fP option does not work with NNTP.  The \-\fBB\fP option
  1930. Xwill only work with NNTP if the \fInnmaster\fP is running on the NNTP
  1931. Xserver.
  1932. X.SH FILES
  1933. XThe $db, $master, and $news names used below are synonyms for the
  1934. XDB_DIRECTORY, MASTER_DIRECTORY, and NEWS_LIB_DIRECTORY defined during
  1935. Xconfiguration.
  1936. X.LP
  1937. X.DT
  1938. X.ta \w'$db/DATA/\fInnn\fP.[dx]'u+3m
  1939. X.\"ta 0 20
  1940. X$db/MASTER    Database master index
  1941. X.br
  1942. X$db/GROUPS    News group names and flags in MASTER file order
  1943. X.br
  1944. X$db/DATA/\fInnn\fP.[dx]    Database files for group number \fInnn\fP
  1945. X.br
  1946. X.../.nn[dx]    Database files if located in the group directories
  1947. X.br
  1948. X$master/GATE    Message channel from \fInnadmin\fP to \fInnmaster\fP
  1949. X.br
  1950. X$master/MPID    The process id of the \fInnmaster\fP daemon.
  1951. X.br
  1952. X$Log        The log file (the location is configuration dependent)
  1953. X.br
  1954. X$news/active    Existing articles and groups
  1955. X.br
  1956. X/usr/lib/nntp_server    Contains the name of the NNTP server.
  1957. X.DT
  1958. X.LP
  1959. XThe MASTER file contains a record for each news group, occurring in
  1960. Xthe same sequence as the group names in the GROUPS file.  The sequence
  1961. Xalso defines the group numbers used to identify the files in the
  1962. Xdatabase's DATA directory.
  1963. X.LP
  1964. XThe GATE file will be created by \fInnadmin\fP when needed, and
  1965. Xremoved by \fInnmaster\fP when it has read it.  Therefore, to send a
  1966. Xmessage to the \fInnmaster\fP requires that you are allowed to write
  1967. Xin the $master directory.
  1968. X.LP
  1969. XThe contents of the Log file are described in the \fInnadmin\fP manual.
  1970. X.SH SEE ALSO
  1971. Xnn(1), nncheck(1), nngrep(1), nntidy(1)
  1972. X.br
  1973. Xnnadmin(1M), nnquery(1M), nnusage(1M), nnmaster(8)
  1974. X.SH AUTHOR
  1975. XKim F. Storm, Texas Instruments A/S, Denmark
  1976. X.br
  1977. XE-mail: storm@texas.dk
  1978. END_OF_FILE
  1979.   if test 21325 -ne `wc -c <'man/nnmaster.8'`; then
  1980.     echo shar: \"'man/nnmaster.8'\" unpacked with wrong size!
  1981.   fi
  1982.   # end of 'man/nnmaster.8'
  1983. fi
  1984. echo shar: End of archive 13 \(of 22\).
  1985. cp /dev/null ark13isdone
  1986. MISSING=""
  1987. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ; do
  1988.     if test ! -f ark${I}isdone ; then
  1989.     MISSING="${MISSING} ${I}"
  1990.     fi
  1991. done
  1992. if test "${MISSING}" = "" ; then
  1993.     echo You have unpacked all 22 archives.
  1994.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1995. else
  1996.     echo You still must unpack the following archives:
  1997.     echo "        " ${MISSING}
  1998. fi
  1999. exit 0
  2000.  
  2001. exit 0 # Just in case...
  2002.